Skip to main content

Data Fetching

What is Loader

Shuvi provides a feature to fetch initial data when server-side rendering or route navigation for route component.
Each route module can export a component and a loader.

The loader is a function that receives a content object parameter containing current url and request information and several methods and returns a data object, a redirecting response or an error response.

The hook useLoaderData will provide the data that loader returns.

Take a look at the simple example as the following:

import { useLoaderData, Loader } from "@shuvi/runtime";

const RouteComponent = () => {
const data = useLoaderData<LoaderData>();
return (
<div>
<p>{data.hello}</p>
</div>
);
};

type LoaderData = {
hello: string;
};

async function doSomethingAsync() {
// do something async
}

export const loader: Loader<LoaderData> = async ({
isServer,
pathname,
query,
params,
redirect,
error,
appContext,
req,
}) => {
await doSomethingAsync();
return {
hello: "world",
};
};

export default RouteComponent;

Import loader From Other Modules

danger

If you need to import a loader from another module, please ensure that the module is relatively independent.

Otherwise, it will affect the performance of the application.

Examples of correct code:

// anyModule.js
// only export the loader module.
export const loader = function () {
return "someData";
};
// page.js or layout.js
export { loader } from "./anyModule"; // Only re-export the loader.

Examples of incorrect code:

// anyModule.js
// ✅ OK - It is safe to define a single loader function in the anyModule.js.
export const loader = function () {
return "someData";
};
// ❌ Wrong - Don't define and export modules related to views.
export default function Page() {
return <div>page content</div>;
}
// ❌ Wrong - Don't define and export modules that are unrelated to the loader.
export const otherModule = "otherModule";
// page.js or layout.js
// ❌ Wrong - Don't re-export default and loader from the same file.
export { default, loader } from "./anyModule";

// ✅ OK - It is safe to export a single loader function.
export { loader } from "./anyModule";

Timing of Executing Loader

The loader is isomorphic and it will run before every server-side or client-side initial rendering and every route navigation to make sure the returned data is ready when component renders.

By default, when using server-side rendering, all loaders will run at server-side and the result of loaders will be inserted to the HTML document, so that during hydrating, the result of loader can be reused and loader will not run at client side.

On navigation in the browser, loader will run at client side before route navigation complete.

Shuvi allows nested routes. All loaders of matched route modules will run in parallel.

When a route navigation is triggered, only the loader of the newly rendered route component, the loader of the route component that remains rendered but the params have changed, and the loader of the last matched route component will be executed.

caution

Since loaders may be executed on the server or client, please make sure they are isomorphic.

During hot module reloading, all loaders will be re-executed on the client side. The loaderContext on the client side does not contain the req object. If the loader contains codes that are not compatible with the client side, the application will throw an error.

Return Type of Loader

Loader should return a serializable object or a specific response indicating redirecting or throwing error.

Serializable Object

It is the most common situation that loader returns an object which is route component needed.

But please note the returned object must be serializable because the returned data will be serialized as string and will be inserted in the HTML that send to the browser so that the data can be reused while hydrating.

import { Loader } from "@shuvi/runtime";

type LoaderData = {
hello: string;
};

export const loader: Loader<LoaderData> = async () => {
return {
hello: "world",
};
};

Redirecting Response

It is allowed that loader returns a response indicating redirecting to another path or url.
A redirecting response can be created by redirect method on loader parameter object as the following example:

import { Loader } from "@shuvi/runtime";
export const loader: Loader = async ({ redirect }) => {
return redirect("/target", 301);
};

Error Response

It is also allowed that loader returns a response that redirects to an error page with error statusCode.
An error response can be created by error method on loader parameter object as the following example:

import { Loader } from "@shuvi/runtime";
export const loader: Loader = async ({ error }) => {
return error("error message", 404);
};

Exception of Running Loader

If loader throws an error or causes an unhandled rejection, shuvi will handle this unexpected error.

On server side, shuvi will send a blank HTML and fallback to client-side rendering.

On client side, shuvi will automatically redirect to the error page.

Loader Type

Please refer to Reference / Loader